from fastapi import FastAPI, Response, Depends, HTTPException, status
from fastapi.security import OAuth2PasswordBearer, OAuth2PasswordRequestForm
from fastapi.middleware.cors import CORSMiddleware
from sqlalchemy.orm import Session, joinedload
from datetime import datetime, timedelta
from jose import JWTError, jwt
from passlib.context import CryptContext
from typing import Optional
import tempfile
import os
import subprocess
import secrets
from models import SessionLocal, User, Book, Word, LearningProgress, UserRole, create_tables
from pydantic import BaseModel
from dotenv import load_dotenv
import requests
from fastapi.responses import StreamingResponse
import io

from english.config import FFMPEG_PATH
from sqlalchemy import func

# 加载环境变量
load_dotenv()

app = FastAPI()

# 配置CORS
app.add_middleware(
    CORSMiddleware,
    allow_origins=["*"],
    allow_credentials=True,
    allow_methods=["*"],
    allow_headers=["*"],
)

# 创建数据库表
create_tables()

# 密码加密
pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")

# JWT配置
SECRET_KEY = os.getenv("SECRET_KEY", "your-secret-key-fallback")
ALGORITHM = "HS256"
ACCESS_TOKEN_EXPIRE_MINUTES = 30

oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")

# 响应模型
class ResponseModel(BaseModel):
    code: int = 200
    msg: Optional[str] = None
    data: Optional[dict | list] = None

# 用户模型
class UserCreate(BaseModel):
    username: str
    password: str
    email: str

class UserLogin(BaseModel):
    username: str
    password: str

class Token(BaseModel):
    access_token: str
    token_type: str

class TokenData(BaseModel):
    username: Optional[str] = None

# 工具函数
def verify_password(plain_password, hashed_password):
    return pwd_context.verify(plain_password, hashed_password)

def get_password_hash(password):
    return pwd_context.hash(password)

def create_access_token(data: dict, expires_delta: Optional[timedelta] = None):
    to_encode = data.copy()
    if expires_delta:
        expire = datetime.utcnow() + expires_delta
    else:
        expire = datetime.utcnow() + timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES)
    to_encode.update({"exp": expire})
    encoded_jwt = jwt.encode(to_encode, SECRET_KEY, algorithm=ALGORITHM)
    return encoded_jwt

async def get_current_user(token: str = Depends(oauth2_scheme)):
    credentials_exception = HTTPException(
        status_code=status.HTTP_401_UNAUTHORIZED,
        detail="无法验证凭据",
        headers={"WWW-Authenticate": "Bearer"},
    )
    db = SessionLocal()
    try:
        payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM])
        username: str = payload.get("sub")
        if username is None:
            raise credentials_exception
        token_data = TokenData(username=username)
        user = db.query(User).options(joinedload(User.learning_progress)).filter(User.username == token_data.username).first()
        if user is None:
            raise credentials_exception
        return user
    except JWTError:
        raise credentials_exception
    finally:
        db.close()

# 登录接口
@app.post("/login", response_model=ResponseModel)
async def login(form_data: OAuth2PasswordRequestForm = Depends()):
    db = SessionLocal()
    try:
        user = db.query(User).filter(User.username == form_data.username).first()
        if not user or not verify_password(form_data.password, user.password):
            return ResponseModel(code=401, msg="用户名或密码错误")
        
        # 更新最后登录时间
        user.last_login = datetime.utcnow()
        db.commit()
        
        # 创建访问令牌
        access_token_expires = timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES)
        access_token = create_access_token(
            data={"sub": user.username}, expires_delta=access_token_expires
        )
        
        return ResponseModel(
            code=200,
            msg="登录成功",
            data={
                "access_token": access_token,
                "token_type": "bearer",
                "username": user.username
            }
        )
    except Exception as e:
        db.rollback()
        return ResponseModel(code=500, msg=f"登录失败: {str(e)}")
    finally:
        db.close()

# 获取当前用户信息
@app.get("/user/me", response_model=ResponseModel)
async def read_users_me(current_user: User = Depends(get_current_user)):
    return ResponseModel(
        code=200,
        data={
            "username": current_user.username,
            "email": current_user.email,
            "role": current_user.role
        }
    )

# 获取所有书本
@app.get("/books", response_model=ResponseModel)
async def get_books(current_user: User = Depends(get_current_user)):
    db = SessionLocal()
    try:
        books = db.query(Book).all()
        return ResponseModel(
            code=200,
            data=[
                {
                    "id": book.id,
                    "name": book.name,
                    "description": book.description,
                    "is_learning": any(
                        progress.book_id == book.id and not progress.is_completed
                        for progress in current_user.learning_progress
                    )
                }
                for book in books
            ]
        )
    except Exception as e:
        return ResponseModel(code=500, msg=f"获取书本列表失败: {str(e)}")
    finally:
        db.close()

# 开始学习书本
@app.post("/books/{book_id}/start", response_model=ResponseModel)
async def start_book(book_id: int, current_user: User = Depends(get_current_user)):
    db = SessionLocal()
    try:
        # 检查书本是否存在
        book = db.query(Book).filter(Book.id == book_id).first()
        if not book:
            return ResponseModel(code=404, msg="书本不存在")
        
        # 检查是否已经在学习这本书
        existing_progress = db.query(LearningProgress).filter(
            LearningProgress.user_id == current_user.id,
            LearningProgress.book_id == book_id,
            LearningProgress.is_completed == False
        ).first()
        
        if existing_progress:
            return ResponseModel(code=400, msg="您已经在学习这本书")
        
        # 获取书本的第一个单词
        first_word = db.query(Word).filter(Word.book_id == book_id).order_by(Word.id).first()
        if not first_word:
            return ResponseModel(code=404, msg="这本书还没有单词")
        
        # 创建新的学习进度
        new_progress = LearningProgress(
            user_id=current_user.id,
            book_id=book_id,
            current_word_id=first_word.id
        )
        db.add(new_progress)
        db.commit()
        
        return ResponseModel(code=200, msg="开始学习成功")
    except Exception as e:
        db.rollback()
        return ResponseModel(code=500, msg=f"开始学习失败: {str(e)}")
    finally:
        db.close()

# 获取当前单词
@app.get("/current-word", response_model=ResponseModel)
async def get_current_word(current_user: User = Depends(get_current_user)):
    db = SessionLocal()
    try:
        # 获取用户当前学习进度
        progress = db.query(LearningProgress).filter(
            LearningProgress.user_id == current_user.id,
            LearningProgress.is_completed == False
        ).first()
        
        if not progress:
            return ResponseModel(code=404, msg="请先选择要学习的书本")
        
        # 获取当前单词
        word = db.query(Word).filter(Word.id == progress.current_word_id).first()
        if not word:
            return ResponseModel(code=404, msg="单词不存在")
        
        return ResponseModel(
            code=200,
            data={
                "id": word.id,
                "word": word.word,
                "accent": word.accent,
                "mean_cn": word.mean_cn,
                "sentence": word.sentence,
                "sentence_trans": word.sentence_trans,
                "word_etyma": word.word_etyma
            }
        )
    except Exception as e:
        return ResponseModel(code=500, msg=f"获取当前单词失败: {str(e)}")
    finally:
        db.close()

# 获取上一个单词
@app.get("/previous-word", response_model=ResponseModel)
async def previous_word(current_user: User = Depends(get_current_user)):
    db = SessionLocal()
    try:
        # 获取用户当前学习进度
        progress = db.query(LearningProgress).filter(
            LearningProgress.user_id == current_user.id,
            LearningProgress.is_completed == False
        ).first()
        
        if not progress:
            return ResponseModel(code=404, msg="请先选择要学习的书本")
        
        # 获取当前单词
        current_word = db.query(Word).filter(Word.id == progress.current_word_id).first()
        if not current_word:
            return ResponseModel(code=404, msg="当前单词不存在")
        
        # 获取上一个单词
        previous_word = db.query(Word).filter(
            Word.book_id == progress.book_id,
            Word.id < current_word.id
        ).order_by(Word.id.desc()).first()
        
        if not previous_word:
            return ResponseModel(code=404, msg="已经是第一个单词")
        
        # 更新学习进度
        progress.current_word_id = previous_word.id
        progress.last_studied = datetime.utcnow()
        db.commit()
        
        return ResponseModel(
            code=200,
            data={
                "id": previous_word.id,
                "word": previous_word.word,
                "accent": previous_word.accent,
                "mean_cn": previous_word.mean_cn,
                "sentence": previous_word.sentence,
                "sentence_trans": previous_word.sentence_trans,
                "word_etyma": previous_word.word_etyma
            }
        )
    except Exception as e:
        db.rollback()
        return ResponseModel(code=500, msg=f"获取上一个单词失败: {str(e)}")
    finally:
        db.close()

# 获取下一个单词
@app.post("/next-word", response_model=ResponseModel)
async def next_word(current_user: User = Depends(get_current_user)):
    db = SessionLocal()
    try:
        # 获取用户当前学习进度
        progress = db.query(LearningProgress).filter(
            LearningProgress.user_id == current_user.id,
            LearningProgress.is_completed == False
        ).first()
        
        if not progress:
            return ResponseModel(code=404, msg="请先选择要学习的书本")
        
        # 获取当前单词
        current_word = db.query(Word).filter(Word.id == progress.current_word_id).first()
        if not current_word:
            return ResponseModel(code=404, msg="当前单词不存在")
        
        # 获取下一个单词
        next_word = db.query(Word).filter(
            Word.book_id == progress.book_id,
            Word.id > current_word.id
        ).order_by(Word.id).first()
        
        if not next_word:
            # 如果没有下一个单词，标记为已完成
            progress.is_completed = True
            db.commit()
            return ResponseModel(code=404, msg="恭喜！您已完成本书的学习")
        
        # 更新学习进度
        progress.current_word_id = next_word.id
        progress.last_studied = datetime.utcnow()
        db.commit()
        
        return ResponseModel(
            code=200,
            data={
                "id": next_word.id,
                "word": next_word.word,
                "accent": next_word.accent,
                "mean_cn": next_word.mean_cn,
                "sentence": next_word.sentence,
                "sentence_trans": next_word.sentence_trans,
                "word_etyma": next_word.word_etyma
            }
        )
    except Exception as e:
        db.rollback()
        return ResponseModel(code=500, msg=f"获取下一个单词失败: {str(e)}")
    finally:
        db.close()

# 获取单词发音
@app.get("/pronounce")
async def pronounce_word(text: str):
    try:
        # 调用文字转语音API
        response = requests.get(f"https://dict.youdao.com/dictvoice?audio={text}&type=2")
        if response.status_code != 200:
            return ResponseModel(code=500, msg="获取发音失败")
        
        # 返回音频流
        return StreamingResponse(
            io.BytesIO(response.content),
            media_type="audio/mpeg"
        )
    except Exception as e:
        return ResponseModel(code=500, msg=f"获取发音失败: {str(e)}")



# 获取中文发音
@app.get("/pronounce-cn")
async def pronounce_cn(text: str):
    try:

        response = requests.get(f"https://dict.youdao.com/dictvoice?audio=test&type=1")
        if response.status_code != 200:
            return ResponseModel(code=500, msg="获取发音失败")
        # 返回音频流
        return  StreamingResponse(
            io.BytesIO(response.content),
            media_type="audio/mpeg"
        )
    except Exception as e:
        return ResponseModel(code=500, msg=f"获取发音失败: {str(e)}")

# 重置管理员密码
@app.post("/reset-admin-password", response_model=ResponseModel)
def reset_admin_password(
    new_password: str,
):
    db = SessionLocal()
    try:
        admin = db.query(User).filter(User.username == "admin").first()
        if not admin:
            return ResponseModel(code=404, msg="管理员用户不存在")
        
        admin.password = get_password_hash(new_password)
        db.commit()
        return ResponseModel(code=200, msg="管理员密码重置成功")
    except Exception as e:
        db.rollback()
        return ResponseModel(code=500, msg=f"重置管理员密码失败: {str(e)}")
    finally:
        db.close()

if __name__ == "__main__":
    import uvicorn
    uvicorn.run("app:app", host="0.0.0.0", port=8000, reload=True) 